{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tracing and Observability\n",
    "\n",
    "AutoGen has [built-in support for tracing](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/telemetry.html) and observability for collecting comprehensive records on the execution of your application. This feature is useful for debugging, performance analysis, and understanding the flow of your application.\n",
    "\n",
    "This capability is powered by the [OpenTelemetry](https://opentelemetry.io/) library, which means you can use any OpenTelemetry-compatible backend to collect and analyze traces.\n",
    "\n",
    "AutoGen follows the [OpenTelemetry Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/) for tracing, for agents and tools.\n",
    "It also follows the [Semantic Conventions for GenAI Systems](https://opentelemetry.io/docs/specs/semconv/gen-ai/) currently under development.\n",
    "\n",
    "## Setup\n",
    "\n",
    "To begin, you need to install the OpenTelemetry Python package. You can do this using pip:\n",
    "\n",
    "```bash\n",
    "pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-grpc opentelemetry-instrumentation-openai\n",
    "```\n",
    "\n",
    "Once you have the SDK installed, the simplest way to set up tracing in AutoGen is to:\n",
    "\n",
    "1. Configure an OpenTelemetry tracer provider\n",
    "2. Set up an exporter to send traces to your backend\n",
    "3. Connect the tracer provider to the AutoGen runtime\n",
    "\n",
    "## Telemetry Backend\n",
    "\n",
    "To collect and view traces, you need to set up a telemetry backend. Several open-source options are available, including Jaeger, Zipkin. For this example, we will use Jaeger as our telemetry backend.\n",
    "\n",
    "For a quick start, you can run Jaeger locally using Docker:\n",
    "\n",
    "```bash\n",
    "docker run -d --name jaeger \\\n",
    "  -e COLLECTOR_OTLP_ENABLED=true \\\n",
    "  -p 16686:16686 \\\n",
    "  -p 4317:4317 \\\n",
    "  -p 4318:4318 \\\n",
    "  jaegertracing/all-in-one:latest\n",
    "```\n",
    "\n",
    "This command starts a Jaeger instance that listens on port 16686 for the Jaeger UI and port 4317 for the OpenTelemetry collector. You can access the Jaeger UI at `http://localhost:16686`.\n",
    "\n",
    "## Tracing an AgentChat Team\n",
    "\n",
    "In the following section, we will review how to enable tracing with an AutoGen GroupChat team. The AutoGen runtime already supports open telemetry (automatically logging message metadata). To begin, we will create a tracing service that will be used to instrument the AutoGen runtime.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Overriding of current TracerProvider is not allowed\n",
      "Attempting to instrument while already instrumented\n"
     ]
    }
   ],
   "source": [
    "from opentelemetry import trace\n",
    "from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter\n",
    "from opentelemetry.instrumentation.openai import OpenAIInstrumentor\n",
    "from opentelemetry.sdk.resources import Resource\n",
    "from opentelemetry.sdk.trace import TracerProvider\n",
    "from opentelemetry.sdk.trace.export import BatchSpanProcessor\n",
    "\n",
    "# Set up telemetry span exporter.\n",
    "otel_exporter = OTLPSpanExporter(endpoint=\"http://localhost:4317\", insecure=True)\n",
    "span_processor = BatchSpanProcessor(otel_exporter)\n",
    "\n",
    "# Set up telemetry trace provider.\n",
    "tracer_provider = TracerProvider(resource=Resource({\"service.name\": \"autogen-test-agentchat\"}))\n",
    "tracer_provider.add_span_processor(span_processor)\n",
    "trace.set_tracer_provider(tracer_provider)\n",
    "\n",
    "# Instrument the OpenAI Python library\n",
    "OpenAIInstrumentor().instrument()\n",
    "\n",
    "# we will get reference this tracer later using its service name\n",
    "# tracer = trace.get_tracer(\"autogen-test-agentchat\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All of the code to create a [team](./tutorial/teams.ipynb) should already be familiar to you.\n",
    "\n",
    "```{note}\n",
    "AgentChat teams are run using the AutoGen Core's agent runtime.\n",
    "In turn, the runtime is already instrumented to log, see [Core Telemetry Guide](../core-user-guide/framework/telemetry.md).\n",
    "To disable the agent runtime telemetry, you can set the `trace_provider` to\n",
    "`opentelemetry.trace.NoOpTracerProvider` in the runtime constructor.\n",
    "\n",
    "Additionally, you can set the environment variable `AUTOGEN_DISABLE_RUNTIME_TRACING` to `true` to disable the agent runtime telemetry if you don't have access to the runtime constructor. For example, if you are using `ComponentConfig`.\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from autogen_agentchat.agents import AssistantAgent\n",
    "from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination\n",
    "from autogen_agentchat.teams import SelectorGroupChat\n",
    "from autogen_agentchat.ui import Console\n",
    "from autogen_core import SingleThreadedAgentRuntime\n",
    "from autogen_ext.models.openai import OpenAIChatCompletionClient\n",
    "\n",
    "\n",
    "def search_web_tool(query: str) -> str:\n",
    "    if \"2006-2007\" in query:\n",
    "        return \"\"\"Here are the total points scored by Miami Heat players in the 2006-2007 season:\n",
    "        Udonis Haslem: 844 points\n",
    "        Dwayne Wade: 1397 points\n",
    "        James Posey: 550 points\n",
    "        ...\n",
    "        \"\"\"\n",
    "    elif \"2007-2008\" in query:\n",
    "        return \"The number of total rebounds for Dwayne Wade in the Miami Heat season 2007-2008 is 214.\"\n",
    "    elif \"2008-2009\" in query:\n",
    "        return \"The number of total rebounds for Dwayne Wade in the Miami Heat season 2008-2009 is 398.\"\n",
    "    return \"No data found.\"\n",
    "\n",
    "\n",
    "def percentage_change_tool(start: float, end: float) -> float:\n",
    "    return ((end - start) / start) * 100\n",
    "\n",
    "\n",
    "async def main() -> None:\n",
    "    model_client = OpenAIChatCompletionClient(model=\"gpt-4o\")\n",
    "\n",
    "    # Get a tracer with the default tracer provider.\n",
    "    tracer = trace.get_tracer(\"tracing-autogen-agentchat\")\n",
    "\n",
    "    # Use the tracer to create a span for the main function.\n",
    "    with tracer.start_as_current_span(\"run_team\"):\n",
    "        planning_agent = AssistantAgent(\n",
    "            \"PlanningAgent\",\n",
    "            description=\"An agent for planning tasks, this agent should be the first to engage when given a new task.\",\n",
    "            model_client=model_client,\n",
    "            system_message=\"\"\"\n",
    "            You are a planning agent.\n",
    "            Your job is to break down complex tasks into smaller, manageable subtasks.\n",
    "            Your team members are:\n",
    "                WebSearchAgent: Searches for information\n",
    "                DataAnalystAgent: Performs calculations\n",
    "\n",
    "            You only plan and delegate tasks - you do not execute them yourself.\n",
    "\n",
    "            When assigning tasks, use this format:\n",
    "            1. <agent> : <task>\n",
    "\n",
    "            After all tasks are complete, summarize the findings and end with \"TERMINATE\".\n",
    "            \"\"\",\n",
    "        )\n",
    "\n",
    "        web_search_agent = AssistantAgent(\n",
    "            \"WebSearchAgent\",\n",
    "            description=\"An agent for searching information on the web.\",\n",
    "            tools=[search_web_tool],\n",
    "            model_client=model_client,\n",
    "            system_message=\"\"\"\n",
    "            You are a web search agent.\n",
    "            Your only tool is search_tool - use it to find information.\n",
    "            You make only one search call at a time.\n",
    "            Once you have the results, you never do calculations based on them.\n",
    "            \"\"\",\n",
    "        )\n",
    "\n",
    "        data_analyst_agent = AssistantAgent(\n",
    "            \"DataAnalystAgent\",\n",
    "            description=\"An agent for performing calculations.\",\n",
    "            model_client=model_client,\n",
    "            tools=[percentage_change_tool],\n",
    "            system_message=\"\"\"\n",
    "            You are a data analyst.\n",
    "            Given the tasks you have been assigned, you should analyze the data and provide results using the tools provided.\n",
    "            If you have not seen the data, ask for it.\n",
    "            \"\"\",\n",
    "        )\n",
    "\n",
    "        text_mention_termination = TextMentionTermination(\"TERMINATE\")\n",
    "        max_messages_termination = MaxMessageTermination(max_messages=25)\n",
    "        termination = text_mention_termination | max_messages_termination\n",
    "\n",
    "        selector_prompt = \"\"\"Select an agent to perform task.\n",
    "\n",
    "        {roles}\n",
    "\n",
    "        Current conversation context:\n",
    "        {history}\n",
    "\n",
    "        Read the above conversation, then select an agent from {participants} to perform the next task.\n",
    "        Make sure the planner agent has assigned tasks before other agents start working.\n",
    "        Only select one agent.\n",
    "        \"\"\"\n",
    "\n",
    "        task = \"Who was the Miami Heat player with the highest points in the 2006-2007 season, and what was the percentage change in his total rebounds between the 2007-2008 and 2008-2009 seasons?\"\n",
    "\n",
    "        runtime = SingleThreadedAgentRuntime(\n",
    "            tracer_provider=trace.NoOpTracerProvider(),  # Disable telemetry for runtime.\n",
    "        )\n",
    "        runtime.start()\n",
    "\n",
    "        team = SelectorGroupChat(\n",
    "            [planning_agent, web_search_agent, data_analyst_agent],\n",
    "            model_client=model_client,\n",
    "            termination_condition=termination,\n",
    "            selector_prompt=selector_prompt,\n",
    "            allow_repeated_speaker=True,\n",
    "            runtime=runtime,\n",
    "        )\n",
    "        await Console(team.run_stream(task=task))\n",
    "\n",
    "        await runtime.stop()\n",
    "\n",
    "    await model_client.close()\n",
    "\n",
    "\n",
    "# asyncio.run(main())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---------- TextMessage (user) ----------\n",
      "Who was the Miami Heat player with the highest points in the 2006-2007 season, and what was the percentage change in his total rebounds between the 2007-2008 and 2008-2009 seasons?\n",
      "---------- TextMessage (PlanningAgent) ----------\n",
      "To find the information requested, we need to follow these steps:\n",
      "\n",
      "1. Identify the Miami Heat player with the highest points during the 2006-2007 season.\n",
      "2. Get the total rebounds for that player in both the 2007-2008 and 2008-2009 seasons.\n",
      "3. Calculate the percentage change in total rebounds between these two seasons.\n",
      "\n",
      "Here are the tasks assigned to achieve this:\n",
      "\n",
      "1. WebSearchAgent: Find the Miami Heat player with the highest points during the 2006-2007 season.\n",
      "2. WebSearchAgent: After identifying the player, find the total rebounds for that player in the 2007-2008 and 2008-2009 seasons.\n",
      "3. DataAnalystAgent: Calculate the percentage change in the player's total rebounds between the 2007-2008 and 2008-2009 seasons.\n",
      "---------- ToolCallRequestEvent (WebSearchAgent) ----------\n",
      "[FunctionCall(id='call_hS8yod9l6CYUllDveUffp58e', arguments='{\"query\":\"Miami Heat leading scorer 2006-2007 season\"}', name='search_web_tool')]\n",
      "---------- ToolCallExecutionEvent (WebSearchAgent) ----------\n",
      "[FunctionExecutionResult(content='Here are the total points scored by Miami Heat players in the 2006-2007 season:\\n        Udonis Haslem: 844 points\\n        Dwayne Wade: 1397 points\\n        James Posey: 550 points\\n        ...\\n        ', name='search_web_tool', call_id='call_hS8yod9l6CYUllDveUffp58e', is_error=False)]\n",
      "---------- ToolCallSummaryMessage (WebSearchAgent) ----------\n",
      "Here are the total points scored by Miami Heat players in the 2006-2007 season:\n",
      "        Udonis Haslem: 844 points\n",
      "        Dwayne Wade: 1397 points\n",
      "        James Posey: 550 points\n",
      "        ...\n",
      "        \n",
      "---------- ToolCallRequestEvent (WebSearchAgent) ----------\n",
      "[FunctionCall(id='call_bUJxtpxUXFSxECDogye9WL0g', arguments='{\"query\":\"Dwyane Wade total rebounds in 2007-2008 season\"}', name='search_web_tool')]\n",
      "---------- ToolCallExecutionEvent (WebSearchAgent) ----------\n",
      "[FunctionExecutionResult(content='The number of total rebounds for Dwayne Wade in the Miami Heat season 2007-2008 is 214.', name='search_web_tool', call_id='call_bUJxtpxUXFSxECDogye9WL0g', is_error=False)]\n",
      "---------- ToolCallSummaryMessage (WebSearchAgent) ----------\n",
      "The number of total rebounds for Dwayne Wade in the Miami Heat season 2007-2008 is 214.\n",
      "---------- ToolCallRequestEvent (WebSearchAgent) ----------\n",
      "[FunctionCall(id='call_pgYNSDhhyodtteot56FRktxp', arguments='{\"query\":\"Dwyane Wade total rebounds in 2008-2009 season\"}', name='search_web_tool')]\n",
      "---------- ToolCallExecutionEvent (WebSearchAgent) ----------\n",
      "[FunctionExecutionResult(content='The number of total rebounds for Dwayne Wade in the Miami Heat season 2008-2009 is 398.', name='search_web_tool', call_id='call_pgYNSDhhyodtteot56FRktxp', is_error=False)]\n",
      "---------- ToolCallSummaryMessage (WebSearchAgent) ----------\n",
      "The number of total rebounds for Dwayne Wade in the Miami Heat season 2008-2009 is 398.\n",
      "---------- ToolCallRequestEvent (DataAnalystAgent) ----------\n",
      "[FunctionCall(id='call_A89acjYHlNDLzG09rVNJ0J6H', arguments='{\"start\":214,\"end\":398}', name='percentage_change_tool')]\n",
      "---------- ToolCallExecutionEvent (DataAnalystAgent) ----------\n",
      "[FunctionExecutionResult(content='85.98130841121495', name='percentage_change_tool', call_id='call_A89acjYHlNDLzG09rVNJ0J6H', is_error=False)]\n",
      "---------- ToolCallSummaryMessage (DataAnalystAgent) ----------\n",
      "85.98130841121495\n",
      "---------- TextMessage (PlanningAgent) ----------\n",
      "The Miami Heat player with the highest points during the 2006-2007 season was Dwyane Wade, who scored 1,397 points. \n",
      "\n",
      "The total rebounds for Dwyane Wade in the 2007-2008 season were 214, and in the 2008-2009 season, they were 398.\n",
      "\n",
      "The percentage change in his total rebounds between these two seasons is approximately 86.0%.\n",
      "\n",
      "TERMINATE\n"
     ]
    }
   ],
   "source": [
    "await main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can then use the Jaeger UI to view the traces collected from the application run above.  \n",
    "\n",
    "![Jaeger UI](jaeger.png)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
